﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

//------
using System.Net;
using System.Net.Sockets;
using System.Threading;
//------

namespace eeb_2012_clientserver_client
{

    // State object for receiving data from remote device.
    public class StateObject
    {
        // Client socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 256;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder sb = new StringBuilder();

        // ConnectionHandler
        public ConnectionHandler chandler = null;
    }

    public class ConnectionHandler
    {
        private StateObject state;
        public bool connected = false;
        private String ipAddress;
        private Int32 port;
        public ManualResetEvent doneDone = new ManualResetEvent(false);
        public ManualResetEvent sendDone = new ManualResetEvent(false);
        public ManualResetEvent receiveDone = new ManualResetEvent(false);

        public static ArrayList conArr = null;

        public ConnectionHandler(StateObject stateObj)
        {
            if (ConnectionHandler.conArr == null)
            {
                ConnectionHandler.conArr = new ArrayList();
            }

            state = stateObj;
            connected = true;
            ipAddress = ((IPEndPoint)state.workSocket.RemoteEndPoint).Address.ToString();
            port = ((IPEndPoint)state.workSocket.RemoteEndPoint).Port;
            ConnectionHandler.conArr.Add(this);
            //Console.WriteLine("------------ New Connection Handler created. Processing...");
        }

        public void sendThread()
        {
            while (true && state.workSocket.Connected && connected)
            {
                sendDone.Reset();
                Client.Send(state.workSocket, "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuh" +
                                              "MuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhMuhM<EOF>", state);
                sendDone.WaitOne();
                printConnectionList();
                Thread.Sleep(200000);
            }
        }

        public void receiveThread()
        {
            while (true && state.workSocket.Connected && connected)
            {
                receiveDone.Reset();
                Client.Receive(state.workSocket, state);
                receiveDone.WaitOne();
                Thread.Sleep(1);
            }

        }

        public void setReceiveDone()
        {
            receiveDone.Set();
        }

        public void setSendDone()
        {
            sendDone.Set();
        }

        public String getIpString()
        {
            return ipAddress;
        }

        public Int32 getPortInt()
        {
            return port;
        }

        public void closeConnection()
        {
            connected = false;
            ConnectionHandler.conArr.Remove(this);
            Console.WriteLine("------------ ConnectionHandler closed: {0}:{1}", getIpString(), getPortInt());
        }

        public void printConnectionList()
        {
            foreach (ConnectionHandler cHandl in ConnectionHandler.conArr)
            {
                Console.WriteLine("@@ {0}:{1}", cHandl.getIpString(), cHandl.getPortInt());
            }
        }
    }

    public class Client
    {
        // The port number for the remote device.
        private const int port = 2000;

        // ManualResetEvent instances signal completion.
        private static ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private static ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private static ManualResetEvent receiveDone =
            new ManualResetEvent(false);

        // The response from the remote device.
        private static String response = String.Empty;

        private static void StartClient()
        {
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
                // The name of the 
                // remote device is "host.contoso.com".
                //IPHostEntry ipHostInfo = Dns.Resolve("localhost");
                //IPAddress ipAddress = ipHostInfo.AddressList[0];
                IPAddress ipAddress = IPAddress.Loopback;
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP socket.
                Socket client = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);

                // Connect to the remote endpoint.
                client.BeginConnect(remoteEP,
                    new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();

                Console.WriteLine("Eingabe:");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Create the state object.
                StateObject state = new StateObject();

                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;
                state.workSocket = client;

                // Complete the connection.
                client.EndConnect(ar);

                Console.WriteLine("Socket connected to {0}",
                    client.RemoteEndPoint.ToString());

                // Create new connection handler.
                ConnectionHandler chandler = new ConnectionHandler(state);
                state.chandler = chandler;

                Thread receiveing = new Thread(chandler.receiveThread);
                receiveing.Start();

                Thread sending = new Thread(chandler.sendThread);
                sending.Start();

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static void Receive(Socket client, StateObject state)
        {
            try
            {
                // Begin receiving the data from the remote device.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            try
            {
                // Retrieve the state object and the client socket 
                // from the asynchronous state object.
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;

                // Read data from the remote device.
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    Console.WriteLine("bytesRead: {0}", bytesRead);
                    // There might be more data, so store the data received so far.
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                    // Check for end-of-file tag. If it is not there, read 
                    // more data.
                    content = state.sb.ToString();

                    if (content.IndexOf("<EOF>") > -1)
                    {
                        // All the data has been read from the 
                        // client. Display it on the console.
                        state.chandler.setReceiveDone();
                        Console.Write("---------- {0}:{1}: ", IPAddress.Parse(((IPEndPoint)client.RemoteEndPoint).Address.ToString()), ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
                        Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
                            content.Length, content);

                        // Clear data for next message.
                        state.sb.Clear();
                    }
                    else
                    {
                        // Get the rest of the data.
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(ReceiveCallback), state);
                    }

                }
                else
                {
                    // All the data has arrived; put it in response.
                    if (state.sb.Length > 1)
                    {
                        response = state.sb.ToString();
                        Console.WriteLine(" Data: {0}", response);
                        state.sb.Clear();
                    }
                    // Signal that all bytes have been received.
                    //receiveDone.Set();
                    state.chandler.setReceiveDone();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static void Send(Socket client, String data, StateObject state)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), state);
        }

        private static void SendCallback(IAsyncResult ar)
        {
            StateObject state = (StateObject)ar.AsyncState;
            // Retrieve the socket from the state object.
            Socket client = state.workSocket;
            
            try
            {
                // Retrieve the socket from the state object.
                //Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = client.EndSend(ar);
                Console.WriteLine("----------\nSent {0} bytes to server.", bytesSent);

                // Signal that all bytes have been sent.
                //sendDone.Set();
                // Signal Thread.
                state.chandler.setSendDone();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        static void Main(string[] args)
        {
            StartClient();
        }
    }
}
